package com.ruoyi.pay.wx;

import com.alibaba.fastjson.JSON;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.pay.AbstractPayService;
import com.ruoyi.pay.config.PayProperties;
import com.ruoyi.pay.exception.PayException;
import com.ruoyi.pay.model.PayNotifyResultModel;
import com.ruoyi.pay.model.PayRequestModel;
import com.ruoyi.pay.model.PayResultModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@RequiredArgsConstructor
@Service("wxPayService")
public class WxPayService extends AbstractPayService {
    private static final Map<String, InputStream> certFileMap = new ConcurrentHashMap<String, InputStream>();
    private final PayProperties payProperties;

    @PostConstruct
    @Override
    public void init() {
        log.info("微信支付配置装配");
        payProperties.getWx().forEach(payProperties -> {
            WxPayApiConfig wxConfig = WxPayApiConfig.builder()
                .slAppId(payProperties.getSlAppId())
                .slMchId(payProperties.getSlMchId())
                .appId(payProperties.getWxAppId())
                .certPath(payProperties.getWxCertPath())
                .certP12Path(payProperties.getWxCert12Path())
                .keyPath(payProperties.getWxKeyPath())
                .partnerKey(payProperties.getWxV2Key())
                .apiKey(payProperties.getWxV2Key())
                .apiKey3(payProperties.getWxV3Key())
                .mchId(payProperties.getWxMchId())
                .domain(payProperties.getDomain())
                .build();
            WxPayApiConfigKit.putApiConfig(wxConfig);

            try {
                InputStream is = certFile(payProperties.getWxCert12Path());
                if (certFileMap.get(payProperties.getWxAppId()) != null) {
                    certFileMap.remove(payProperties.getWxAppId());
                    certFileMap.put(payProperties.getWxAppId(), is);
                } else {
                    certFileMap.put(payProperties.getWxAppId(), is);
                }
            } catch (Exception e) {
                log.info(e.getMessage());
            }
        });
//        wxMap = paymsWxPayConfigVoList.stream().collect(Collectors.toMap(s -> s.getWxAppId(), a -> a));
//        log.info("微信支付配置装配");
//        payProperties.getWx().forEach(wxPayProperties -> {
//            WxPayApiConfig wxConfig = WxPayApiConfig.builder()
//                .slAppId(wxPayProperties.getSlAppId())
//                .slMchId(wxPayProperties.getSlMchId())
//                .appId(wxPayProperties.getAppId())
//                .certPath(wxPayProperties.getCertPath())
//                .certP12Path(wxPayProperties.getCert12Path())
//                .keyPath(wxPayProperties.getKeyPath())
//                .partnerKey(wxPayProperties.getApiV2Key())
//                .apiKey(wxPayProperties.getApiV2Key())
//                .apiKey3(wxPayProperties.getApiV3Key())
//                .mchId(wxPayProperties.getMchId())
//                .domain(wxPayProperties.getDomain())
//                .build();
//            WxPayApiConfigKit.putApiConfig(wxConfig);
//        });
    }

    public InputStream certFile(String certFilePath) throws Exception {
        File file = new File(certFilePath);
        InputStream certStream = new FileInputStream(file);
        return certStream;
    }

    @Override
    public PayResultModel pay(PayRequestModel payRequestModel) {
        PayResultModel payResultModel = new PayResultModel();
        try {
            String appId = payRequestModel.getAppId();
            WxPayApiConfig wxPayApiConfig = null;
            if (StringUtils.isNotEmpty(appId)) {
                wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
            } else {
                wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
            }
            // 置换成分为单位
            BigDecimal totalFee = payRequestModel.getTotalFee().multiply(new BigDecimal("100"));
            String fee = String.valueOf(totalFee.longValue());
            String outTradeNo = StringUtils.isBlank(payRequestModel.getOutTradeNo()) ? WxPayKit.generateStr() : payRequestModel.getOutTradeNo();
            Map<String, String> params = null;
            // H5支付
            if (TradeType.MWEB == payRequestModel.getTradeType()) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payRequestModel.getBody())
                    .out_trade_no(outTradeNo)
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(payProperties.getByWxAppId(appId).getWxPayNotifyUrl())
                    .trade_type(payRequestModel.getTradeType().getTradeType())
                    .openid(payRequestModel.getWxOpenId())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);
                String xmlResult = WxPayApi.pushOrder(false, params);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
                log.info("微信支付结果:{}", xmlResult);
                String returnCode = result.get("return_code");
                String returnMsg = result.get("return_msg");
                if (!WxPayKit.codeIsOk(returnCode)) {
                    throw new PayException(returnMsg);
                }
                String resultCode = result.get("result_code");
                if (!WxPayKit.codeIsOk(resultCode)) {
                    throw new PayException(returnMsg);
                }
                // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
                String prepayId = result.get("prepay_id");
                String webUrl = result.get("mweb_url");
                log.info("prepay_id:" + prepayId + " mweb_url:" + webUrl);
                payResultModel.getPackageParams().put("prepay_id", prepayId);
                payResultModel.getPackageParams().put("webUrl", webUrl);
            }
            //小程序支付
            else if (TradeType.JSAPI == payRequestModel.getTradeType()) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payRequestModel.getBody())
//                    .attach("连线客支付")
                    .out_trade_no(outTradeNo)
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(payProperties.getByWxAppId(appId).getWxPayNotifyUrl())
                    .trade_type(TradeType.JSAPI.getTradeType())
                    .openid(payRequestModel.getWxOpenId())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);
                String xmlResult = WxPayApi.pushOrder(false, params);
                log.info("小程序支付参数：{},结果:{}", params, xmlResult);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
                String returnCode = result.get("return_code");
                String returnMsg = result.get("return_msg");
                if (!WxPayKit.codeIsOk(returnCode)) {
                    throw new PayException(returnMsg);
                }
                String resultCode = result.get("result_code");
                if (!WxPayKit.codeIsOk(resultCode)) {
                    String errCodeDes = result.get("err_code_des");
                    throw new PayException(errCodeDes);
                }
                // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
                String prepayId = result.get("prepay_id");
                Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                    wxPayApiConfig.getApiKey(), SignType.MD5);
                log.info("小程序支付的参数:{}", packageParams);
                payResultModel.setPackageParams(packageParams);
            }
            // APP支付
            else if (TradeType.APP == payRequestModel.getTradeType()) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payRequestModel.getBody())
                    .attach("连线客支付")
                    .out_trade_no(WxPayKit.generateStr())
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(payProperties.getByWxAppId(appId).getWxPayNotifyUrl())
                    .trade_type(TradeType.APP.getTradeType())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);

                String xmlResult = WxPayApi.pushOrder(false, params);

                log.info("APP支付结果:{}", xmlResult);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

                String returnCode = result.get("return_code");
                String returnMsg = result.get("return_msg");
                if (!WxPayKit.codeIsOk(returnCode)) {
                    throw new PayException(returnMsg);
                }
                String resultCode = result.get("result_code");
                if (!WxPayKit.codeIsOk(resultCode)) {
                    throw new PayException(returnMsg);
                }
                // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
                String prepayId = result.get("prepay_id");
                Map<String, String> packageParams = WxPayKit.appPrepayIdCreateSign(wxPayApiConfig.getAppId(), wxPayApiConfig.getMchId(), prepayId,
                    wxPayApiConfig.getApiKey(), SignType.MD5);
                String jsonStr = JSON.toJSONString(packageParams);
                log.info("返回apk的参数:" + jsonStr);
                payResultModel.setPackageParams(packageParams);
            }

        } catch (Exception e) {
            log.error("微信支付错误", e);
            payResultModel.setSuccess(false);
            payResultModel.setMsg(e.getMessage());
        }
        return payResultModel;
    }

    @Override
    public PayNotifyResultModel payNotify(Map<String, String> params, String appId) {
        PayNotifyResultModel payNotifyResultModel = new PayNotifyResultModel();
        log.info("支付通知={}", params);
        String returnCode = params.get("return_code");
        WxPayApiConfig wxPayApiConfig = null;
        if (StringUtils.isNotEmpty(appId)) {
            wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
        } else {
            wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        }
        // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
        // 注意此处签名方式需与统一下单的签名类型一致
        if (WxPayKit.verifyNotify(params, wxPayApiConfig.getApiKey(), SignType.MD5)) {
            if (WxPayKit.codeIsOk(returnCode)) {
                // 更新订单信息
                // 发送通知等
                Map<String, String> xml = new HashMap<String, String>(2);
                xml.put("return_code", "SUCCESS");
                xml.put("return_msg", "OK");
                payNotifyResultModel.setNotifyResult(WxPayKit.toXml(xml));
            } else {
                payNotifyResultModel.setMsg("回调状态异常" + returnCode);
                payNotifyResultModel.setSuccess(false);
            }
        } else {
            payNotifyResultModel.setMsg("回调签名错误");
            payNotifyResultModel.setSuccess(false);
        }
        return payNotifyResultModel;
    }
}
